/*
 * GoogleAuthorizor provides an abstraction for the process of authorizing the product.
 * It hides the implementation of sending http requests, retrieving authorization 
 * code and access token and storing these information. 
 * 
 */
// @author A0105514U
/** @author A0105514U */

package gCalEngine;

import global.Constant;
import global.GlobalFields;
import global.Methods;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.*;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;

public class GoogleAuthorizor{
	private GoogleTokenResponse response;
	private String authCode;
	private String redirectUrl;
	private Collection<String> scope;
	private JsonFactory factory; 
	private NetHttpTransport transport;
	private GoogleAuthorizationCodeFlow auth;
	private Credential credential;
	
	private Logger logger;
	private FileHandler fh;
	private final String AUTHORIZOR_LOG_FILE = "authorizorLog.txt";
	
	public GoogleAuthorizor(){
		logger = Logger.getLogger(GoogleSynchronizor.class.getName());
		try{
			fh = new FileHandler(AUTHORIZOR_LOG_FILE);
			logger.addHandler(fh);
		}catch(IOException e){
			
		}
		
		scope = new ArrayList<String>();
		scope.add(Constant.APPLICATION_SCOPE);
		factory = new JacksonFactory(); 
		transport = new NetHttpTransport();
		credential = new GoogleCredential();
		auth = null;
	}
	
	public boolean authorize(){
		try{
			logger.log(Level.INFO,"Authorizing...");
			initAuthorizor();
			logger.log(Level.INFO,"Getting authorization code");
			getAuthCode();

			if(authCode.length() != 0){
				logger.log(Level.INFO,"Authorization code Received");
				logger.log(Level.INFO,"Sending access request...");
				sendAccessRequest();
				storeCredential();
				
				logger.log(Level.INFO,"Successfully authorized");
				GlobalFields.IS_GOOGLE_CONNECTED = true;
			}else{
				logger.log(Level.SEVERE,"Authorization failed. Google Disconnected");
				GlobalFields.IS_GOOGLE_CONNECTED = false;
			}
		}catch(Exception e){
			logger.log(Level.SEVERE,"Authorization failed. Google Disconnected");
			GlobalFields.IS_GOOGLE_CONNECTED = false;
		}
		
		return GlobalFields.IS_GOOGLE_CONNECTED;
	}
	
	public JsonFactory getJsonFactory(){
		return factory;
	}
	
	public HttpTransport getHttpTransport(){
		return transport;
	}
	
	public Credential getCredential(){
		return credential;
	}
	
	private void initAuthorizor(){
		if(auth == null){
			auth = new GoogleAuthorizationCodeFlow.Builder(transport,
	                factory, Constant.CLIENT_ID, Constant.CLIENT_SECRET, scope).setAccessType("offline").setApprovalPrompt("force").build();
		}
	}
	
	private void getAuthCode() throws Exception{		
		assert(auth != null);
		VerificationCodeReceiver receiver = new LocalServerReceiver();
		redirectUrl = receiver.getRedirectUri();
		String newUrl = auth.newAuthorizationUrl().setRedirectUri(redirectUrl).build();
		Methods.browse(newUrl);
		authCode = receiver.waitForCode();
		receiver.stop();
	}
	
	private void sendAccessRequest() throws Exception{
		response = auth.newTokenRequest(authCode).setRedirectUri(redirectUrl).execute();
	}
	
	private void storeCredential() throws IOException{
		credential.setFromTokenResponse(response);
		auth.createAndStoreCredential(response, Constant.EMPTY_STRING);
	}
}